package com.eian.boot.metrics.aspect;

import com.eian.boot.common.id.IdHelper;
import com.eian.boot.metrics.collector.PerformanceCollector;
import com.eian.boot.metrics.config.MetricsProperties;
import com.eian.boot.metrics.model.PerformanceRecord;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.time.LocalDateTime;
import java.util.concurrent.ThreadLocalRandom;
import java.util.regex.Pattern;

/**
 * 包扫描性能监控AOP切面
 * 基于配置的包路径和类名模式进行监控
 *
 * @author alex.meng
 */
@Slf4j
@Aspect
@Component
@Order(1001) // 优先级低于注解切面
@ConditionalOnProperty(prefix = "eian.metrics", name = "enabled", havingValue = "true", matchIfMissing = true)
public class PackageScanMonitorAspect {

    private final MetricsProperties metricsProperties;

    @Autowired(required = false)
    private PerformanceCollector performanceCollector;

    public PackageScanMonitorAspect(MetricsProperties metricsProperties) {
        this.metricsProperties = metricsProperties;
    }

    /**
     * 定义切点：匹配配置的包路径，但排除性能监控相关的类
     */
    @Pointcut("execution(* com.eian..*(..)) && !within(com.eian.boot.metrics..*)")
    public void packagePointcut() {
    }

    /**
     * 拦截包扫描范围内的方法
     */
    @Around("packagePointcut()")
    public Object monitorPackageMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        // 检查全局开关
        if (!metricsProperties.isEnabled()) {
            return joinPoint.proceed();
        }

        // 检查包扫描开关
        if (!metricsProperties.getScan().isEnabled()) {
            return joinPoint.proceed();
        }

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Class<?> targetClass = signature.getDeclaringType();

        // 检查是否应该监控此方法
        if (!shouldMonitorMethod(targetClass, method)) {
            return joinPoint.proceed();
        }

        // 采样检查
        if (!shouldSample()) {
            return joinPoint.proceed();
        }

        return executeWithMonitoring(joinPoint);
    }

    private boolean shouldMonitorMethod(Class<?> targetClass, Method method) {
        String className = targetClass.getSimpleName();
        String methodName = method.getName();
        String fullClassName = targetClass.getName();

        // 首先检查方法是否已有@PerformanceMonitor注解，如果有则跳过包扫描监控
        if (method.isAnnotationPresent(com.eian.boot.metrics.annotation.PerformanceMonitor.class)) {
            return false;
        }

        // 检查类是否有@PerformanceMonitor注解，如果有则跳过包扫描监控
        if (targetClass.isAnnotationPresent(com.eian.boot.metrics.annotation.PerformanceMonitor.class)) {
            return false;
        }

        // 检查包路径
        if (!isInConfiguredPackages(fullClassName)) {
            return false;
        }

        // 检查类名模式
        if (!matchesClassPatterns(className)) {
            return false;
        }

        // 检查方法名模式
        if (!matchesMethodPatterns(methodName)) {
            return false;
        }

        // 检查方法修饰符
        int modifiers = method.getModifiers();

        // 检查是否监控私有方法
        if (Modifier.isPrivate(modifiers) && !metricsProperties.getScan().getIncludeClassPatterns().contains(".*Private.*")) {
            return false;
        }

        // 检查是否监控静态方法
        if (Modifier.isStatic(modifiers)) {
            // 这里可以根据配置决定是否监控静态方法
            // 暂时允许监控静态方法
        }

        // 检查最小执行时间阈值（这里只能预估，实际会在执行后检查）
        // 对于包扫描，我们先执行再判断是否记录

        return true;
    }

    private boolean isInConfiguredPackages(String fullClassName) {
        var basePackages = metricsProperties.getScan().getBasePackages();

        // 如果没有配置包路径，则不进行包扫描监控
        // 这样确保只有显式配置了包路径才会启用包扫描
        if (basePackages.isEmpty()) {
            return false;
        }

        return basePackages.stream()
                .anyMatch(packagePattern -> matchesPackagePattern(fullClassName, packagePattern));
    }

    private boolean matchesPackagePattern(String fullClassName, String packagePattern) {
        // 支持通配符匹配
        String regex = packagePattern
                .replace(".", "\\.")
                .replace("*", ".*");

        return Pattern.matches(regex, fullClassName);
    }

    private boolean matchesClassPatterns(String className) {
        var includePatterns = metricsProperties.getScan().getIncludeClassPatterns();
        var excludePatterns = metricsProperties.getScan().getExcludeClassPatterns();

        // 先检查排除模式
        for (String excludePattern : excludePatterns) {
            if (Pattern.matches(excludePattern, className)) {
                return false;
            }
        }

        // 再检查包含模式
        for (String includePattern : includePatterns) {
            if (Pattern.matches(includePattern, className)) {
                return true;
            }
        }

        return false;
    }

    private boolean matchesMethodPatterns(String methodName) {
        var includePatterns = metricsProperties.getScan().getIncludeMethodPatterns();
        var excludePatterns = metricsProperties.getScan().getExcludeMethodPatterns();

        // 先检查排除模式
        for (String excludePattern : excludePatterns) {
            if (Pattern.matches(excludePattern, methodName)) {
                return false;
            }
        }

        // 再检查包含模式
        for (String includePattern : includePatterns) {
            if (Pattern.matches(includePattern, methodName)) {
                return true;
            }
        }

        return false;
    }

    private boolean shouldSample() {
        double sampleRate = metricsProperties.getPerformance().getSampleRate();

        if (sampleRate >= 1.0) {
            return true;
        }

        if (sampleRate <= 0.0) {
            return false;
        }

        return ThreadLocalRandom.current().nextDouble() < sampleRate;
    }

    private Object executeWithMonitoring(ProceedingJoinPoint joinPoint) throws Throwable {
        // 创建性能记录
        PerformanceRecord performanceRecord = createPerformanceRecord(joinPoint);

        // 记录开始时间和内存
        LocalDateTime startTime = LocalDateTime.now();
        long startNanos = System.nanoTime();
        PerformanceRecord.MemoryInfo memoryInfo = null;

        if (metricsProperties.getPerformance().isMemoryMonitoring()) {
            memoryInfo = captureMemoryInfo();
        }

        Object result = null;
        try {
            // 执行目标方法
            result = joinPoint.proceed();
            performanceRecord.setSuccess(true);

            // 记录返回值
            if (metricsProperties.getPerformance().isRecordResult()) {
                performanceRecord.setResult(truncateIfNeeded(result));
            }

        } catch (Throwable throwable) {
            performanceRecord.setSuccess(false)
                    .setErrorMessage(throwable.getMessage())
                    .setErrorType(throwable.getClass().getSimpleName());
            throw throwable;
        } finally {
            // 计算执行时间
            long endNanos = System.nanoTime();
            long duration = (endNanos - startNanos) / 1_000_000; // 转换为毫秒
            LocalDateTime endTime = LocalDateTime.now();

            // 完善记录信息
            performanceRecord.setStartTime(startTime)
                    .setEndTime(endTime)
                    .setDuration(duration);

            // 记录内存信息
            if (memoryInfo != null) {
                PerformanceRecord.MemoryInfo endMemoryInfo = captureMemoryInfo();
                memoryInfo.setAfterMemory(endMemoryInfo.getAfterMemory())
                        .setMemoryDelta(endMemoryInfo.getAfterMemory() - memoryInfo.getBeforeMemory());
                performanceRecord.setMemoryInfo(memoryInfo);
            }

            // 记录线程信息
            performanceRecord.setThreadInfo(captureThreadInfo());

            // 收集性能数据 - 仅在启用数据收集时调用
            if (metricsProperties.getCollect().isEnabled() && performanceCollector != null) {
                performanceCollector.collect(performanceRecord);
            }

            // 输出日志
            logPerformanceRecord(performanceRecord);
        }

        return result;
    }

    private PerformanceRecord createPerformanceRecord(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        PerformanceRecord recordInfo = new PerformanceRecord()
                .setId(IdHelper.simpleUuid())
                .setClassName(signature.getDeclaringType().getSimpleName())
                .setMethodName(method.getName())
                .setMethodSignature(method.toString())
                .setName(signature.getDeclaringType().getSimpleName() + "." + method.getName());

        // 记录方法参数
        if (metricsProperties.getPerformance().isRecordArgs()) {
            Object[] args = joinPoint.getArgs();
            if (args != null && args.length > 0) {
                var argList = java.util.Arrays.stream(args)
                        .map(this::truncateIfNeeded)
                        .toList();
                recordInfo.setArgs(argList);
            }
        }

        return recordInfo;
    }

    private Object truncateIfNeeded(Object obj) {
        if (obj == null) {
            return null;
        }

        String str = obj.toString();
        int maxLength = metricsProperties.getPerformance().getMaxRecordLength();

        if (str.length() > maxLength) {
            return str.substring(0, maxLength) + "...";
        }

        return obj;
    }

    private PerformanceRecord.MemoryInfo captureMemoryInfo() {
        Runtime runtime = Runtime.getRuntime();
        long totalMemory = runtime.totalMemory();
        long freeMemory = runtime.freeMemory();
        long usedMemory = totalMemory - freeMemory;
        long maxMemory = runtime.maxMemory();

        return new PerformanceRecord.MemoryInfo()
                .setBeforeMemory(usedMemory)
                .setAfterMemory(usedMemory) // 将在finally块中更新
                .setTotalMemory(totalMemory)
                .setFreeMemory(freeMemory)
                .setMaxMemory(maxMemory);
    }

    private PerformanceRecord.ThreadInfo captureThreadInfo() {
        Thread currentThread = Thread.currentThread();
        return new PerformanceRecord.ThreadInfo()
                .setThreadId(currentThread.threadId())
                .setThreadName(currentThread.getName())
                .setVirtual(currentThread.isVirtual());
    }

    private void logPerformanceRecord(PerformanceRecord performanceRecord) {
        if (!metricsProperties.getLog().isEnabled()) {
            return;
        }

        String level = metricsProperties.getLog().getLevel();
        String message = formatLogMessage(performanceRecord);

        // 检查是否为慢方法
        long slowThreshold = metricsProperties.getPerformance().getSlowThreshold();
        boolean isSlow = performanceRecord.getDuration() > slowThreshold;

        switch (level.toUpperCase()) {
            case "DEBUG":
                log.debug(message);
                break;
            case "WARN":
                log.warn(message);
                break;
            case "ERROR":
                if (!performanceRecord.isSuccess()) {
                    log.error(message);
                }
                break;
            default:
                if (isSlow) {
                    log.warn("[SLOW] {}", message);
                } else {
                    log.info(message);
                }
        }
    }

    private String formatLogMessage(PerformanceRecord performanceRecord) {
        String pattern = metricsProperties.getLog().getPattern();

        return pattern
                .replace("{method}", performanceRecord.getClassName() + "." + performanceRecord.getMethodName())
                .replace("{duration}", String.valueOf(performanceRecord.getDuration()))
                .replace("{success}", String.valueOf(performanceRecord.isSuccess()))
                .replace("{thread}", performanceRecord.getThreadInfo() != null ? performanceRecord.getThreadInfo().getThreadName() : "unknown");
    }
}
